package com.tungse.study.dynamic_proxy;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Tungse
 */
public class DynamicProxyLog {
    private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger(0);
    private static final Map<Class<?>, Object> INSTANCE_MAP = new ConcurrentHashMap<>();

    public static <T> T getInstance(Class<T> classes, T instance) throws Exception {
        if (INSTANCE_MAP.containsKey(classes)) {
            return (T) INSTANCE_MAP.get(classes);
        }
        String className = createClassName(classes);
        File javaFile = createJavaFile(MyInterface.class, className);
        JavaFileCompiler.compiler(javaFile);
        T t = (T) Main.class.getClassLoader().loadClass(classes.getPackage().getName() + "." + className).getConstructor().newInstance();
        t.getClass().getMethod("setInstance", classes).invoke(t, instance);
        INSTANCE_MAP.put(classes, t);
        return t;
    }

    public static File createJavaFile(Class<?> classes, String className) throws IOException {
        StringBuilder sb = new StringBuilder();
        sb.append("package ").append(classes.getPackage().getName()).append(";\n\n");
        sb.append("import ").append(classes.getName()).append(";\n\n");
        sb.append("public class ").append(className).append(" implements ").append(classes.getName()).append(" {\n\n");
        sb.append("private static ").append(classes.getSimpleName()).append(" instance;\n");
        sb.append("public void setInstance(").append(classes.getSimpleName()).append(" instance){\n\n");
        sb.append("     this.instance = instance;\n");
        sb.append("}\n");

        // 3. 方法实现
        for (Method method : classes.getDeclaredMethods()) {
            if (Modifier.isStatic(method.getModifiers()) || Modifier.isFinal(method.getModifiers())) {
                continue;
            }
            sb.append("    @Override\n");
            sb.append("    public ").append(method.getReturnType().getSimpleName())
                    .append(" ").append(method.getName()).append("(");
            // 3.1 参数列表
            List<String> parameterTypes = new ArrayList<>();
            List<String> parameterNames = new ArrayList<>();
            for (Parameter param : method.getParameters()) {
                parameterTypes.add(param.getType().getSimpleName() + " arg" + parameterNames.size());
                parameterNames.add("arg" + parameterNames.size());
            }
            sb.append(String.join(", ", parameterTypes))
                    .append(") {\n");
            // 3.2 方法体（打印签名）
            sb.append("        System.out.println(\"begin \");\n");

            // 3.3 方法体（调用原始方法）
            String returnArgName = "a" + UUID.randomUUID().toString().replace("-", "").substring(0, 5); // 返回值参数名
            if (!"void".equals(method.getReturnType().getSimpleName())) {
                sb.append("        ").append(method.getReturnType().getSimpleName()).append(" ").append(returnArgName).append(" = ");
            }
            sb.append("        instance.").append(method.getName()).append("(");
            if (parameterNames.isEmpty()) {
                sb.append(");\n");
            } else {
                sb.append(String.join(", ", parameterNames));
                sb.append(");\n");
            }

            sb.append("        System.out.println(\"end \");\n ");
            if (!"void".equals(method.getReturnType().getSimpleName())) {
                sb.append("        return ").append(returnArgName).append(";\n");
            }
            sb.append("   }\n");
        }

        sb.append("}\n");
        File javaFile = new File(className + ".java");
        Files.writeString(javaFile.toPath(), sb.toString());

        return javaFile;
    }


    private static String createClassName(Class<?> classes) {
        return classes.getSimpleName() + "$Log"
                //+ UUID.randomUUID().toString().replace("-", "").substring(0,5)
                + ATOMIC_INTEGER.incrementAndGet();
    }
}
